package com.cloudkinto.service.redpacket.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloudkinto.appletservice.finance.vo.WxPayNotifyRequest;
import com.cloudkinto.common.DicBusinessItemConstant;
import com.cloudkinto.common.constant.GlobalConstants;
import com.cloudkinto.common.constant.SysConstant;
import com.cloudkinto.common.exception.BizException;
import com.cloudkinto.common.utils.TimeUtils;
import com.cloudkinto.common.web.PageResult;
import com.cloudkinto.common.web.SingleResult;
import com.cloudkinto.dao.*;
import com.cloudkinto.entity.*;
import com.cloudkinto.service.redpacket.WxRedPacketService;
import com.cloudkinto.service.redpacket.vo.WinPrizeInfoReq;
import com.cloudkinto.service.redpacket.vo.WxPaySuccessResponse;
import com.cloudkinto.service.user.vo.WxPowerEntity;
import com.cloudkinto.utils.AesUtil;
import com.cloudkinto.utils.RSAEncryptor;
import okhttp3.*;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sun.misc.BASE64Decoder;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.net.ssl.*;
import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * service实现类
 * </p>
 *
 * @author ding
 * @since 2021-02-05
 */
@Service
@Transactional(rollbackFor = Exception.class)//事务回滚
public class WxRedPacketServiceImpl extends ServiceImpl<WxRedPacketDao, WxRedPacketDo> implements WxRedPacketService {
    private static final long RED_PACKET_EXPIRE = 60 * 60 * 24 * 5;
    private static final String WX_TOTAL_PACKET = "WX_TOTAL_PACKET";
    @Autowired
    private WxRedPacketDao dao;
    @Autowired
    private WxUserDao wxUserDao;
    @Autowired
    private DicBusinessItemDao dicBusinessItemDao;
    @Autowired
    private UserDao userDao;
    @Autowired
    private CompanyDao companyDao;
    @Autowired
    private RedisTemplate redisTemplate;
    private String WX_POWER_ID = "WX_POWER_ID:";
    @Autowired
    private ExchangeRateDao exchangeRateDao;
    @Autowired
    private RechargeRecordDao rechargeRecordDao;
    @Autowired
    private CompanyAccountDao companyAccountDao;
    @Value("${gbc.template.signPath}")
    private String signPath;
    @Autowired
    private ThreadPoolTaskExecutor executor;

    @Override
    public SingleResult addInit() {
        return SingleResult.success("");
    }

    @Override
    public Object add(Object req, Long userId) {
        WxRedPacketDo entityDo = new WxRedPacketDo();
        BeanUtils.copyProperties(req, entityDo);
        handlerAddBefore(entityDo, userId);
        dao.insert(entityDo);
        return handlerAddAfter(entityDo);
    }

    //新增校验
    private void addCheck(Object req) {

    }

    //做新增之前要执行的业务定制处理
    private void handlerAddBefore(WxRedPacketDo entityDo, Long userId) {
        entityDo.setCreateTime(new Date());
    }

    //将新增的do转换成要返回的实体
    private Object handlerAddAfter(WxRedPacketDo entityDo) {
        Object res = new Object();
        BeanUtils.copyProperties(entityDo, res);
        return res;
    }

    @Override
    public SingleResult updateInit(Long id) {
        return SingleResult.success("");
    }

    @Override
    public Object update(Object req, Long userId) {
        /*WxRedPacketDo entityDo = dao.selectById(req.getId());
        BeanUtils.copyProperties(req, entityDo);
        handlerUpdateBefore(entityDo, userId);
        dao.updateById(entityDo);
        return handlerUpdateAfter(entityDo);*/
        return null;
    }

    //编辑校验
    private void updateCheck(Object req) {

    }

    //做修改之前要执行的业务定制处理
    private void handlerUpdateBefore(WxRedPacketDo entityDo, Long userId) {
    }

    //做修改之后要执行的业务定制处理
    private Object handlerUpdateAfter(WxRedPacketDo entityDo) {
        Object res = new Object();
        BeanUtils.copyProperties(entityDo, res);
        return res;
    }

    @Override
    public int delete(Long id, Long userId) {
        WxRedPacketDo entityDo = dao.selectById(id);
        handlerDeleteBefore(entityDo, userId);
        return dao.deleteById(id);
    }

    //做删除之前要执行的业务定制处理
    private void handlerDeleteBefore(WxRedPacketDo entityDo, Long userId) {
    }

    @Override
    public Object detail(Long id) {
        WxRedPacketDo entityDo = dao.selectById(id);
        Object res = new Object();
        BeanUtils.copyProperties(entityDo, res);
        return handlerDetailAfter(res);
    }

    //查询明细之后要执行的业务定制处理
    private Object handlerDetailAfter(Object res) {
        return res;
    }

    @Override
    public SingleResult pageInit() {
        return SingleResult.success("");
    }

    @Override
    public List<Object> getList(Map<String, String> map) {
        QueryWrapper<WxRedPacketDo> wrapper = queryBuild(map);
        List<WxRedPacketDo> list = this.list(wrapper);
        List<Object> result = handlerListAfter(list);
        return result;
    }

    @Override
    public PageResult getListPage(Map<String, String> map) {
        QueryWrapper<WxRedPacketDo> wrapper = queryBuild(map);
        IPage<WxRedPacketDo> page = pageInit(map);
        page = this.page(page, wrapper);
        List<Object> resList = handlerListAfter(page.getRecords());
        PageResult result = new PageResult(resList, (int) page.getCurrent(), (int) page.getSize(), (int) page.getTotal(), (int) page.getPages());
        return result;
    }

    //查询分页列表之后对列表数据的处理
    private List<Object> handlerListAfter(List<WxRedPacketDo> list) {
        List<Object> result = new ArrayList<>();
        return result;
    }

    private IPage<WxRedPacketDo> pageInit(Map<String, String> map) {
        IPage<WxRedPacketDo> page = new Page();
        page.setSize(StringUtils.isNotBlank(map.get("pageSize")) ? Long.parseLong(map.get("pageSize")) : 10);
        page.setCurrent(StringUtils.isNotBlank(map.get("current")) ? Long.parseLong(map.get("current")) : 1);
        return page;
    }

    private QueryWrapper<WxRedPacketDo> queryBuild(Map<String, String> map) {
        QueryWrapper<WxRedPacketDo> wrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(map.get("keyword"))) {
            wrapper.like("keyword", map.get("keyword"));
        }
        return wrapper;
    }


    @Override
    public SingleResult addCount(String friendId, String myId, String openId) {
        if (myId == null) {
            return SingleResult.failure("300", "请先关注公众号");
        }
        WxPowerEntity wxPowerEntity = new WxPowerEntity();
        WxPowerEntity myEntity = new WxPowerEntity();
        String jsonObject = (String) redisTemplate.opsForValue().get(WX_POWER_ID + friendId);
        String myJsonObject = (String) redisTemplate.opsForValue().get(WX_POWER_ID + myId);
        myEntity = JSONObject.parseObject(myJsonObject, WxPowerEntity.class);
        wxPowerEntity = JSONObject.parseObject(jsonObject, WxPowerEntity.class);
        if (wxPowerEntity == null) throw new BizException("助力失败");
        if (wxPowerEntity.getPowerBy().contains(openId)) {
            return SingleResult.failure("500", "已经助力过了");
        }
        if (myEntity.getHelpCount() > 2) throw new BizException("助力次数已达上限");
        if (wxPowerEntity.getCount() < 20) {
            wxPowerEntity.setCount(wxPowerEntity.getCount() + 1);
            wxPowerEntity.getPowerBy().add(openId);
            myEntity.setHelpCount(myEntity.getHelpCount() + 1);
        } else {
            return SingleResult.failure("500", "该好友被助力次数已达上限");
        }
        redisTemplate.opsForValue().set(WX_POWER_ID + friendId, JSONObject.toJSONString(wxPowerEntity));
        redisTemplate.opsForValue().set(WX_POWER_ID + myId, JSONObject.toJSONString(myEntity));

        return SingleResult.success("助力成功", null);
    }

    @Override
    public SingleResult openPacket(String id, String openId) {
        verifyTime();
        if (openId == null) {
            return SingleResult.failure("300", "请先关注公众号");
        }
        WxPowerEntity wxPowerEntity = new WxPowerEntity();
        String jsonObject = (String) redisTemplate.opsForValue().get(WX_POWER_ID + id);
        wxPowerEntity = JSONObject.parseObject(jsonObject, WxPowerEntity.class);
        if (wxPowerEntity == null) {
            wxPowerEntity = new WxPowerEntity();
            LambdaQueryWrapper<WxUserDo> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(WxUserDo::getOpenid, openId);
            wrapper.eq(WxUserDo::getUserType, SysConstant.DELETE_FLAG_FALSE);
            WxUserDo wxUserDo = wxUserDao.selectOne(wrapper);
            if (wxUserDo == null) throw new BizException("301", "请重新打开页面");
            wxPowerEntity.setOpenid(openId);
            wxPowerEntity.setId(wxUserDo.getId());
            wxPowerEntity.setCount(3);
            if (wxPowerEntity.getPowerBy() == null)
                wxPowerEntity.setPowerBy(new ArrayList<>());
            wxPowerEntity.getPowerBy().add(openId);
            redisTemplate.opsForValue().set(WX_POWER_ID + wxUserDo.getId(), JSONObject.toJSONString(wxPowerEntity), RED_PACKET_EXPIRE, TimeUnit.SECONDS);
        } else {
            wxPowerEntity = JSONObject.parseObject(jsonObject, WxPowerEntity.class);
        }
        if (wxPowerEntity.getCount() - wxPowerEntity.getUsedCount() > 0) {
            wxPowerEntity.setUsedCount(wxPowerEntity.getUsedCount() + 1);
        } else {
            return SingleResult.failure("500", "次数已用完");
        }
        int money = getPacketMoney(openId);
        redisTemplate.opsForValue().set(WX_POWER_ID + id, JSONObject.toJSONString(wxPowerEntity), RED_PACKET_EXPIRE, TimeUnit.SECONDS);
        return SingleResult.success(money);
    }

    private int getPacketMoney(String openId) {
//        double rate1 = 0.001;
        LambdaQueryWrapper<WxRedPacketDo> winWrapper = new LambdaQueryWrapper<>();
        Integer totalAmount = (Integer) redisTemplate.opsForValue().get(WX_TOTAL_PACKET);
        if (totalAmount != null && totalAmount.intValue() > 150000)
            throw new BizException("奖品已发完");
        winWrapper.eq(WxRedPacketDo::getOpenId, openId);
        int winCount = dao.selectCount(winWrapper);
        if (winCount > 2) return 0;
        Random random = new Random();
        int count = random.nextInt(100);
        int money = 0;
        if (count <= 5) {
            money = 288;
        } else if (count <= 8) {
            money = 588;
        } else if (count <= 10) {
            money = 888;
        }
        if (totalAmount == null) {
            totalAmount = money;
        } else {
            totalAmount = totalAmount + money;
        }
        redisTemplate.opsForValue().set(WX_TOTAL_PACKET, totalAmount, RED_PACKET_EXPIRE, TimeUnit.SECONDS);
        if (money == 288 || money == 588 || money == 888) {
            LambdaQueryWrapper<WxUserDo> userWrapper = new LambdaQueryWrapper<>();
            userWrapper.eq(WxUserDo::getOpenid, openId);
            WxUserDo wxUserDo = wxUserDao.selectOne(userWrapper);
            WxRedPacketDo redPacketDo = new WxRedPacketDo();
            redPacketDo.setWxId(wxUserDo.getId());
            redPacketDo.setOpenId(openId);
            redPacketDo.setAmount(money);
            redPacketDo.setCreateTime(new Date());
            redPacketDo.setPayFlag(SysConstant.DELETE_FLAG_FALSE);
            save(redPacketDo);
            return money;
        } else {
            return 0;
        }

    }

    @Override
    public SingleResult openAmount(String id, String openId) {
        verifyTime();
        if (openId == null) {
            return SingleResult.failure("300", "请先关注公众号");
        }
        WxPowerEntity wxPowerEntity = new WxPowerEntity();
        String jsonObject = (String) redisTemplate.opsForValue().get(WX_POWER_ID + id);
        wxPowerEntity = JSONObject.parseObject(jsonObject, WxPowerEntity.class);
        if (wxPowerEntity == null) {
            wxPowerEntity = new WxPowerEntity();
            LambdaQueryWrapper<WxUserDo> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(WxUserDo::getOpenid, openId);
            wrapper.eq(WxUserDo::getUserType, SysConstant.DELETE_FLAG_FALSE);
            WxUserDo wxUserDo = wxUserDao.selectOne(wrapper);
            if (wxUserDo == null) throw new BizException("301", "请重新打开页面");
            wxPowerEntity.setOpenid(openId);
            wxPowerEntity.setId(wxUserDo.getId());
            wxPowerEntity.setCount(5);
            if (wxPowerEntity.getPowerBy() == null)
                wxPowerEntity.setPowerBy(new ArrayList<>());
            wxPowerEntity.getPowerBy().add(openId);
            redisTemplate.opsForValue().set(WX_POWER_ID + wxUserDo.getId(), JSONObject.toJSONString(wxPowerEntity), RED_PACKET_EXPIRE, TimeUnit.SECONDS);
        } else {
            wxPowerEntity = JSONObject.parseObject(jsonObject, WxPowerEntity.class);
        }
        if (wxPowerEntity.getCount() - wxPowerEntity.getUsedCount() > 0) {
            wxPowerEntity.setUsedCount(wxPowerEntity.getUsedCount() + 1);
        } else {
            return SingleResult.failure("500", "次数已用完");
        }
        int money = getAmountMoney(openId);
        redisTemplate.opsForValue().set(WX_POWER_ID + id, JSONObject.toJSONString(wxPowerEntity), RED_PACKET_EXPIRE, TimeUnit.SECONDS);
        return SingleResult.success(money);
    }

    private int getAmountMoney(String openId) {
        LambdaQueryWrapper<WxRedPacketDo> winWrapper = new LambdaQueryWrapper<>();
        winWrapper.eq(WxRedPacketDo::getOpenId, openId);
        int winCount = dao.selectCount(winWrapper);
        int money = 0;
        if (winCount > 4) return 0;
        Random random = new Random();
        int count = random.nextInt(100);
        if (count <= 15) {
            money = 288;
        } else if (count <= 25) {
            money = 588;
        } else if (count <= 30) {
            money = 888;
        }
        if (money == 288 || money == 588 || money == 888) {
            LambdaQueryWrapper<WxUserDo> userWrapper = new LambdaQueryWrapper<>();
            userWrapper.eq(WxUserDo::getOpenid, openId);
            WxUserDo wxUserDo = wxUserDao.selectOne(userWrapper);
            WxRedPacketDo redPacketDo = new WxRedPacketDo();
            redPacketDo.setWxId(wxUserDo.getId());
            redPacketDo.setOpenId(openId);
            redPacketDo.setAmount(money);
            redPacketDo.setCreateTime(new Date());
            redPacketDo.setPayFlag(SysConstant.DELETE_FLAG_FALSE);
            save(redPacketDo);
            return money;
        } else {
            return 0;
        }
    }

    public void payAmount() {
//        //添加充值记录

        LambdaQueryWrapper<WxRedPacketDo> userWrapper = new LambdaQueryWrapper<>();
        userWrapper.eq(WxRedPacketDo::getFinishFlag, SysConstant.DELETE_FLAG_FALSE);
        List<WxRedPacketDo> wxUserDo = dao.selectList(userWrapper);
        List<Map> detailList = new ArrayList<>();
        int count = 0;
        int totalAmount = 0;
        for (WxRedPacketDo redPacketDo : wxUserDo) {
            RechargeRecordDo recordDo = createInsertRechargeDo(redPacketDo.getAmount(), getRate(), 0, 0L, redPacketDo.getRealName(), "活动赠送");
            rechargeAmount(recordDo);
            redPacketDo.setFinishFlag(SysConstant.DELETE_FLAG_TRUE);
            updateById(redPacketDo);
        }

    }


    private double getRate() {
        double rate = 0D;
        return exchangeRateDao.getJPRate().getRate();
    }

    private boolean rechargeAmount(RechargeRecordDo recordDo) {
        RechargeRecordDo entityDo = recordDo;
        if (entityDo == null) return false;
        if (entityDo.getStatus().equals("0")) {
            //充值到余额
            QueryWrapper<CompanyAccountDo> amountWrapper = new QueryWrapper();
            amountWrapper.eq("company_id", entityDo.getCompanyId());
            CompanyAccountDo accountDo = companyAccountDao.selectOne(amountWrapper);
            if (accountDo == null) {
                accountDo = new CompanyAccountDo();
                accountDo.setCompanyAccount(entityDo.getAmountJpy());
                accountDo.setCompanyId(entityDo.getCompanyId());
//            accountDo.setCreateBy(req.getCreateBy());
                accountDo.setCreateTime(new Date());
                accountDo.setCreditPoints(0);
                accountDo.setDeleteFlag(SysConstant.DELETE_FLAG_FALSE);
                accountDo.setCreateBy(entityDo.getCreateBy());
                companyAccountDao.insert(accountDo);
            } else {
                accountDo.setCompanyAccount(accountDo.getCompanyAccount() + entityDo.getAmountJpy());
                accountDo.setUpdateTime(new Date());
                accountDo.setDeleteFlag(SysConstant.DELETE_FLAG_FALSE);
                accountDo.setCreateBy(entityDo.getCreateBy());
                companyAccountDao.updateById(accountDo);
            }
            entityDo.setStatus("1");
            entityDo.setUpdateTime(new Date());
            entityDo.setAfterAmount(accountDo.getCompanyAccount());
            recordDo.setEntryTime(new Date());
            recordDo.setEntryBy(1L);
            rechargeRecordDao.updateById(entityDo);
        }
        return true;
    }

    private RechargeRecordDo createInsertRechargeDo(double amount, double rate, int moneytype, Long payType, String companyCode, String tips) {
        RechargeRecordDo entityDo = new RechargeRecordDo();
        entityDo.setId(System.currentTimeMillis());
        entityDo.setRechargeTypeId(payType);//后台充值
        if (moneytype == 0) {
            double amountRMB = amount * rate;
            entityDo.setAmountRmb(new BigDecimal(amountRMB).setScale(2, RoundingMode.HALF_UP).doubleValue());
            entityDo.setAmountJpy(amount);
        } else if (moneytype == 1) {
            double amountJP = amount / rate;
            entityDo.setAmountJpy(new BigDecimal(amountJP).setScale(2, RoundingMode.HALF_UP).doubleValue());
            entityDo.setAmountRmb(amount);
        }
        LambdaQueryWrapper<CompanyDo> companyWrapper = new LambdaQueryWrapper<>();
        companyWrapper.eq(CompanyDo::getCompanyCode, companyCode);
        CompanyDo companyDo = companyDao.selectOne(companyWrapper);
        if (companyDo == null) throw new BizException("充值账户不存在");
        entityDo.setCompanyId(companyDo.getId());
//        entityDo.setCertificateImageFileId(rechargeAddReq.getFileId() == null ? null : Long.parseLong(rechargeAddReq.getFileId()));
        entityDo.setTips(tips);
        entityDo.setJpyRate(rate);
        entityDo.setCreateTime(new Date());
        entityDo.setStatus("0");
        entityDo.setDeleteFlag(SysConstant.DELETE_FLAG_FALSE);
        rechargeRecordDao.insert(entityDo);
        return entityDo;
    }

    @Override
    public void payRedPacket() {
        Map<String, Object> map = new HashMap<>();
        String timeNo = TimeUtils.getCurrentDateFormat("MMddHHmmss");
        map.put("appid", GlobalConstants.APP_ID_WX);
        map.put("out_batch_no", timeNo);
        map.put("batch_name", "筋斗云" + timeNo);
        map.put("batch_remark", "筋斗云红包发放");

        LambdaQueryWrapper<WxRedPacketDo> userWrapper = new LambdaQueryWrapper<>();
        userWrapper.eq(WxRedPacketDo::getFinishFlag, SysConstant.DELETE_FLAG_FALSE);
        List<WxRedPacketDo> wxUserDo = dao.selectList(userWrapper);
        List<Map> detailList = new ArrayList<>();
        int count = 0;
        int totalAmount = 0;
        for (WxRedPacketDo redPacketDo : wxUserDo) {
            if (StringUtils.isBlank(redPacketDo.getRealName())) {
                continue;
            }
            count++;
            totalAmount = totalAmount + redPacketDo.getAmount();
            Map<String, Object> detailMap = new HashMap<>();
            detailMap.put("out_detail_no", timeNo + "d" + count);
            //todo 加密
            try {
//                detailMap.put("user_name", encodeUploadData(redPacketDo.getRealName(), GlobalConstants.ciphertext));
                detailMap.put("user_name", rsaEncryptOAEP(redPacketDo.getRealName(), GlobalConstants.PLATFORM_CERTIFICATE));
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (GeneralSecurityException e) {
                e.printStackTrace();
            }
            detailMap.put("transfer_amount", redPacketDo.getAmount());
            detailMap.put("transfer_remark", "筋斗云物流-红包发放");
            detailMap.put("openid", redPacketDo.getOpenId());
            detailList.add(detailMap);
            redPacketDo.setPayTime(new Date());
            redPacketDo.setFinishFlag(SysConstant.DELETE_FLAG_TRUE);
        }
        if (detailList.size() < 1) return;
        map.put("total_amount", totalAmount);
        map.put("total_num", count);
        map.put("transfer_detail_list", detailList);
        String url = "https://api.mch.weixin.qq.com/v3/transfer/batches";
        String body = okHttpRequestPost(url, JSONObject.toJSONString(map));
        //{"batch_id":"1030001059401253858932021020800062932176","create_time":"2021-02-08T21:08:22+08:00","out_batch_no":"0208210801"}
        if (JSONObject.parseObject(body).getString("batch_id") != null) {
            saveOrUpdateBatch(wxUserDo);
        }
    }

    @Override
    public SingleResult wxPayRecharge(String companyCode, Double money, String openId) {
        String outTradeNo;
        HashMap<String, Object> map = new HashMap<>();
        HashMap<String, Object> resultMap = new HashMap<>();
        map.put("appid", GlobalConstants.APPLet_ID_WX);
        map.put("mchid", GlobalConstants.APP_ID_WX_PAY);
        map.put("description", "筋斗云预充值");
        LambdaQueryWrapper<CompanyDo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CompanyDo::getCompanyCode, companyCode);
        CompanyDo companyDo = companyDao.selectOne(queryWrapper);
        if (companyDo == null) throw new BizException("支付失败，公司账户不存在");
        outTradeNo = companyDo.getCompanyCode() +
                //微信支付单位   人民币 分
                createInsertRechargeDo(money / 100, getRate(), 1, 5L, companyDo.getCompanyCode(), "").getId();
        map.put("out_trade_no", outTradeNo);
        map.put("notify_url", "https://www.cloudkinto.com:8082/api/applet/recharge/wxPayNotify");
        Map<String, Object> amountMap = new HashMap<>();
        amountMap.put("total", money.intValue());
        Map<String, String> payerMap = new HashMap<>();
        payerMap.put("openid", openId);
        map.put("amount", amountMap);
        map.put("payer", payerMap);
        String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
        String body = okHttpRequestPost(url, JSONObject.toJSONString(map));
        //{"batch_id":"1030001059401253858932021020800062932176","create_time":"2021-02-08T21:08:22+08:00","out_batch_no":"0208210801"}
        if (JSONObject.parseObject(body).getString("prepay_id") != null) {
            String prepay_id = JSONObject.parseObject(body).getString("prepay_id");
            long timeStamp = System.currentTimeMillis() / 1000;
            resultMap.put("appid", GlobalConstants.APPLet_ID_WX);
            resultMap.put("timeStamp", timeStamp);
            resultMap.put("nonceStr", UUID.randomUUID().toString().replaceAll("-", ""));
            resultMap.put("package", "prepay_id=" + prepay_id);
            resultMap.put("signType", "RSA");
            resultMap.put("out_trade_no", outTradeNo);
            String originSignData = "";
            originSignData = resultMap.get("appid") + "\n" + resultMap.get("timeStamp") + "\n" +
                    resultMap.get("nonceStr") + "\n" + resultMap.get("package") + "\n";
            System.out.println(originSignData);
            try {
                String signature = sign(originSignData.getBytes("utf-8"));
                System.out.println("paySign:" + signature);
                resultMap.put("paySign", signature);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        //todo 间隔5分对订单状态确认
        executor.execute(() -> {
            try {
                Thread.sleep(1 * 60 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            wxPayRechargeQuery(companyDo.getCompanyCode(), outTradeNo);
        });
        return SingleResult.success(resultMap);
    }

    @Override
    public WxPaySuccessResponse wxPayRechargeQuery(Long companyId, String outTradeNo) {
        String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + outTradeNo + "";
        Map<String, String> map = new HashMap<>();
        map.put("mchid", GlobalConstants.APP_ID_WX_PAY);
        double rate = getRate();
        String body = wxPayRequestGet(url, map);
        WxPaySuccessResponse wxPaySuccessResponse = JSONObject.parseObject(body, WxPaySuccessResponse.class);
        if (wxPaySuccessResponse.getTrade_state() != null && wxPaySuccessResponse.getTrade_state().equals("SUCCESS")) {
            //todo 支付成功，充值入账
            CompanyDo companyDo = companyDao.selectById(companyId);
            if (companyDo == null) return wxPaySuccessResponse;
            if (wxPaySuccessResponse.getOut_trade_no().startsWith(companyDo.getCompanyCode())) {
                String id = wxPaySuccessResponse.getOut_trade_no().replaceFirst(companyDo.getCompanyCode(), "");
                RechargeRecordDo recordDo = rechargeRecordDao.selectById(id);
                rechargeAmount(recordDo);
            }
        } else {
            System.out.println(JSONObject.parseObject(body).getString("code"));
        }
        return wxPaySuccessResponse;
    }

    @Override
    public WxPaySuccessResponse wxPayRechargeQuery(String companyCode, String outTradeNo) {
        LambdaQueryWrapper<CompanyDo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CompanyDo::getCompanyCode, companyCode);
        CompanyDo companyDo = companyDao.selectOne(queryWrapper);
        return wxPayRechargeQuery(companyDo.getId(),outTradeNo);
    }

    @Override
    public SingleResult wxPayNativeRecharge(Long companyId, Double money,String tips) {
        String outTradeNo;
        HashMap<String, Object> map = new HashMap<>();
        HashMap<String, Object> resultMap = new HashMap<>();
        map.put("appid", GlobalConstants.APPLet_ID_WX);
        map.put("mchid", GlobalConstants.APP_ID_WX_PAY);
        map.put("description", "筋斗云预充值（免凭证审核）");
        CompanyDo companyDo = companyDao.selectById(companyId);
        if (companyDo == null) throw new BizException("支付失败，公司账户不存在");
        outTradeNo = companyDo.getCompanyCode() +
                //微信支付单位   人民币 分
                createInsertRechargeDo(money / 100, getRate(), 1, 5L, companyDo.getCompanyCode(), tips).getId();
        map.put("out_trade_no", outTradeNo);
        map.put("notify_url", "https://www.cloudkinto.com:8082/api/applet/recharge/wxPayNotify");
        Map<String, Object> amountMap = new HashMap<>();
        amountMap.put("total", money.intValue());
        map.put("amount", amountMap);
        String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/native";
        String body = okHttpRequestPost(url, JSONObject.toJSONString(map));
        //{
        //	"code_url": "weixin://wxpay/bizpayurl?pr=p4lpSuKzz"
        //}
//        if (JSONObject.parseObject(body).getString("prepay_id") != null) {
//            String prepay_id = JSONObject.parseObject(body).getString("prepay_id");
//            long timeStamp = System.currentTimeMillis() / 1000;
//            resultMap.put("appid", GlobalConstants.APPLet_ID_WX);
//            resultMap.put("timeStamp", timeStamp);
//            resultMap.put("nonceStr", UUID.randomUUID().toString().replaceAll("-", ""));
//            resultMap.put("package", "prepay_id=" + prepay_id);

//            String originSignData = "";
//            originSignData = resultMap.get("appid") + "\n" + resultMap.get("timeStamp") + "\n" +
//                    resultMap.get("nonceStr") + "\n" + resultMap.get("package") + "\n";
//            System.out.println(originSignData);
//            try {
//                String signature = sign(originSignData.getBytes("utf-8"));
//                System.out.println("paySign:" + signature);
//                resultMap.put("paySign", signature);
//            } catch (UnsupportedEncodingException e) {
//                e.printStackTrace();
//            }
//        }
        if (JSONObject.parseObject(body).getString("code_url") != null) {
            resultMap.put("code_url", JSONObject.parseObject(body).getString("code_url"));
            resultMap.put("out_trade_no", outTradeNo);
            //todo 间隔1分对订单状态确认
            executor.execute(() -> {
                try {
                    Thread.sleep(5 * 60 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                wxPayRechargeQuery(companyDo.getCompanyCode(), outTradeNo);
            });
            return SingleResult.success(resultMap);
        }
        return SingleResult.failure(SysConstant.Pay_Error);
    }

    @Override
    public Map wxPayNotify(Map<String, Object> strData, String authorization, String serialNo, String timestamp, String nonce) {
        Map map = new HashMap();
        if (StringUtils.isBlank(authorization)) throw new BizException("签名验证失败");
        if (!serialNo.equals(GlobalConstants.WXPAY_PLATFORM_NO)) {
            getWXxPayCertificates();
        }
        //todo 验证签名
        String originData = timestamp + "\n" + nonce + "\n" + JSONObject.toJSONString(strData) + "\n";
        System.out.println(originData);
        WxPayNotifyRequest request = JSONObject.parseObject(JSONObject.toJSON(strData).toString(), WxPayNotifyRequest.class);
        if (!verify(originData, authorization)) throw new BizException("签名验证失败");
        System.out.println("签名验证成功");
        if (request.getEvent_type().contains("SUCCESS")) {
            AesUtil util = new AesUtil(GlobalConstants.WX_PAY_PRIVATE_KEY.getBytes());
            try {
                String decodeData = util.decryptToString(request.getResource().getAssociated_data().getBytes(), request.getResource().getNonce().getBytes(),
                        request.getResource().getCiphertext());
                if (decodeData != null) {
                    WxPaySuccessResponse response = JSONObject.parseObject(decodeData, WxPaySuccessResponse.class);
                    if (response.getTrade_state().equals("SUCCESS")) {
                        String id = "";
                        if (response.getOut_trade_no().startsWith("C")) {
                            id = response.getOut_trade_no().substring(4);
                        } else if (response.getOut_trade_no().startsWith("11")) {
                            id = response.getOut_trade_no().substring(1);
                        }
                        RechargeRecordDo recordDo = rechargeRecordDao.selectById(id);
                        if (rechargeAmount(recordDo)) {
                            map.put("code", "SUCCESS");
                            map.put("message", "成功");
                        } else {
                            map.put("code", "FAILED");
                            map.put("message", "未查到该充值记录");
                        }
                    }
                }
            } catch (GeneralSecurityException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        return map;
    }

    @Override
    public SingleResult aliPayRecharge(Long companyId, Double money) {

        return null;
    }


    public static String getSignContent(Map<String, String> sortedParams) {
        StringBuilder content = new StringBuilder();
        List<String> keys = new ArrayList(sortedParams.keySet());
        Collections.sort(keys);
        int index = 0;
        Iterator var4 = keys.iterator();

        while (var4.hasNext()) {
            String key = (String) var4.next();
            String value = (String) sortedParams.get(key);
            if (StringUtils.isNotEmpty(key) && StringUtils.isNotEmpty(value)) {
                content.append(index == 0 ? "" : "&").append(key).append("=").append(value);
                ++index;
            }
        }

        return content.toString();
    }

    public String sign(String content, String charset, String privateKey) {
        try {
            if (StringUtils.isEmpty(content)) {
                throw new BizException("待签名内容不可为空");
            } else if (StringUtils.isEmpty(privateKey)) {
                throw new BizException("私钥不可为空");
            } else {
                if (StringUtils.isEmpty(charset)) {
                    charset = "";
                }
                RSAEncryptor rsaEncryptor = new RSAEncryptor();
                return rsaEncryptor.doSign(content, charset, privateKey);
            }
        } catch (Exception var6) {
            String errorMessage = "签名遭遇异常，请检查私钥格式是否正确。" + var6.getMessage() + " content=" + content + "，charset=" + charset + "，privateKeySize=" + privateKey.length();
            throw new BizException(errorMessage, var6);
        }
    }

    private String encodeUploadData(String origionData, String ciphertext) throws GeneralSecurityException, IOException {
        AesUtil util = new AesUtil(GlobalConstants.WX_PAY_PRIVATE_KEY.getBytes());
        String decodeData = util.decryptToString(GlobalConstants.associated_data.getBytes(), GlobalConstants.nonce.getBytes(), ciphertext);
        return rsaEncryptOAEP(origionData, decodeData);
    }

    @Override
    public SingleResult saveUserInfo(WinPrizeInfoReq req) {
        if (StringUtils.isBlank(req.getContent())) throw new BizException("请输入领奖信息");
        req.setContent(req.getContent().trim());
        LambdaQueryWrapper<CompanyDo> companyDoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        companyDoLambdaQueryWrapper.eq(CompanyDo::getCompanyCode, req.getContent());
        CompanyDo companyDo = companyDao.selectOne(companyDoLambdaQueryWrapper);
        if (companyDo == null) throw new BizException("该公司标识不存在");
        LambdaQueryWrapper<WxRedPacketDo> userWrapper = new LambdaQueryWrapper<>();
        userWrapper.eq(WxRedPacketDo::getRealName, req.getContent().trim());
        if (dao.selectCount(userWrapper) >= 5) throw new BizException("该账户已达中奖次数上限");
        userWrapper = new LambdaQueryWrapper<>();
        userWrapper.eq(WxRedPacketDo::getOpenId, req.getOpenId());
        userWrapper.isNull(WxRedPacketDo::getRealName);
        List<WxRedPacketDo> wxUserDo = dao.selectList(userWrapper);
        for (WxRedPacketDo redPacketDo : wxUserDo) {
            redPacketDo.setRealName(req.getContent());
            redPacketDo.setPayFlag(SysConstant.DELETE_FLAG_TRUE);
            LambdaQueryWrapper<WxUserDo> wxUserWrapper = new LambdaQueryWrapper<>();
            wxUserWrapper.eq(WxUserDo::getOpenid, redPacketDo.getOpenId());
            WxUserDo wxUserDo1 = wxUserDao.selectOne(wxUserWrapper);
            if (wxUserDo1 == null) continue;
            wxUserDo1.setCompanyId(companyDo.getId());
            wxUserDao.updateById(wxUserDo1);
            updateById(redPacketDo);
        }
        return SingleResult.success("领取成功", null);
    }

    @Override
    public SingleResult getPrizeInfo(String id, String openId) {
        LambdaQueryWrapper<WxRedPacketDo> userWrapper = new LambdaQueryWrapper<>();
        userWrapper.eq(WxRedPacketDo::getOpenId, openId);
        List<WxRedPacketDo> wxUserDo = dao.selectList(userWrapper);
        return SingleResult.success(wxUserDo);
    }

    @Override
    public SingleResult getHelpInfo(String id, String openId) {
        if (StringUtils.isBlank(id)) throw new BizException("未获取到账号信息");
        String jsonObject = (String) redisTemplate.opsForValue().get(WX_POWER_ID + id);
        WxPowerEntity wxPowerEntity = JSONObject.parseObject(jsonObject, WxPowerEntity.class);
        List<Map> wxUserDoList = new ArrayList<>();
        if (wxPowerEntity == null || wxPowerEntity.getPowerBy() == null || wxPowerEntity.getPowerBy().size() < 1) {
        } else {
            for (String otherOpenId : wxPowerEntity.getPowerBy()) {
                if (otherOpenId.equals(openId)) continue;
                LambdaQueryWrapper<WxUserDo> wxUserDoLambdaQueryWrapper = new LambdaQueryWrapper<>();
                wxUserDoLambdaQueryWrapper.eq(WxUserDo::getOpenid, otherOpenId);
                wxUserDoLambdaQueryWrapper.eq(WxUserDo::getUserType, SysConstant.DELETE_FLAG_FALSE);

                WxUserDo wxUserDo = wxUserDao.selectOne(wxUserDoLambdaQueryWrapper);
                Map map = new HashMap();
                if (wxUserDo == null) continue;
                map.put("nickname", wxUserDo.getNickname());
                map.put("headimgurl", wxUserDo.getHeadimgurl());
                wxUserDoList.add(map);
            }
        }
        return SingleResult.success(wxUserDoList);
    }

    @Override
    public Map setUserInfo(String access_token, String openid) {
        verifyTime();
        String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + access_token + "&openid=" + openid + "&lang=zh_CN";
        String body = okHttpRequestGet(url);
        WxUserDo userInfoEntity = JSONObject.parseObject(body, WxUserDo.class);
//        if (userInfoEntity.getSubscribe() == 0) throw new BizException("300", "请先关注公众号");
        LambdaQueryWrapper<WxUserDo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(WxUserDo::getOpenid, userInfoEntity.getOpenid());
        wrapper.eq(WxUserDo::getUserType, SysConstant.DELETE_FLAG_FALSE);
        WxUserDo wxUserDo = wxUserDao.selectOne(wrapper);
        userInfoEntity.setUserType(SysConstant.DELETE_FLAG_FALSE);
        if (wxUserDo == null) {
            wxUserDao.insert(userInfoEntity);
        } else {
            userInfoEntity.setId(wxUserDo.getId());
            wxUserDao.updateById(userInfoEntity);
        }
        WxPowerEntity wxPowerEntity = new WxPowerEntity();
        String jsonObject = (String) redisTemplate.opsForValue().get(WX_POWER_ID + userInfoEntity.getId());
        if (jsonObject == null) {
            wxPowerEntity.setOpenid(openid);
            wxPowerEntity.setId(userInfoEntity.getId());
            wxPowerEntity.setCount(5);
            if (wxPowerEntity.getPowerBy() == null)
                wxPowerEntity.setPowerBy(new ArrayList<>());
            wxPowerEntity.getPowerBy().add(openid);
            redisTemplate.opsForValue().set(WX_POWER_ID + userInfoEntity.getId(), JSONObject.toJSONString(wxPowerEntity), RED_PACKET_EXPIRE, TimeUnit.SECONDS);
        } else {
            wxPowerEntity = JSONObject.parseObject(jsonObject, WxPowerEntity.class);
        }
        Map hashMap = new HashMap();
        hashMap.put("id", userInfoEntity.getId());
        hashMap.put("count", wxPowerEntity.getCount() - wxPowerEntity.getUsedCount());
        hashMap.put("name", userInfoEntity.getNickname());
        hashMap.put("access_token", access_token);
        hashMap.put("openid", openid);
        hashMap.put("headerUrl", userInfoEntity.getHeadimgurl());
        return hashMap;
    }


    private void verifyTime() {

//        if (new Date().before(TimeUtils.parseDate("2021-02-18 12:00:00", "yyyy-MM-dd HH:mm:ss"))) {
//            throw new BizException("活动未开始");
//        } else if (new Date().after(TimeUtils.parseDate("2021-02-19 22:00:00", "yyyy-MM-dd HH:mm:ss"))) {
//            throw new BizException("活动已结束");
//        }
//        Integer totalAmount = (Integer) redisTemplate.opsForValue().get(WX_TOTAL_PACKET);
//        if (totalAmount != null && totalAmount.intValue() > 100000)
//            throw new BizException("奖品已发完");
    }

    /**
     * 获取微信签名证书-微信端
     */
    @Override
    public void getWXxPayCertificates() {
        String requestUrl = "https://api.mch.weixin.qq.com/v3/certificates";
        String body = wxPayRequestGet(requestUrl, new HashMap<>());
        System.out.println(body);
        //"encrypt_certificate": {
        //				"algorithm": "AEAD_AES_256_GCM",
        //				"associated_data": "certificate",
        //				"ciphertext": "sc8r0ehhp+UgGLJchySmHkE8wCzC8LcYu9UvJUSbxzEQw0SEepWbv3bxvrq/gISvRcHzU/D0X91ZcciUTQpYVqnBpy8kFl84yAJmX1i9FrLkYhCR9A6MEdCgvvZW5+Ibx+cGao8SzktAJtUXVDQHHu3TJv6uoP7sOhQk9R6JxLt60iun1g7r2KKDXjKzC6DGw0HEjD510A4bZ9VJPFWQ0/y0PKU3Ghjw7uh9OQt1p352cO7LzLNqeCzNHCtvAHWfAO1O9w5EI2QnzJSK01NMw9j7spb6B+QX4Q1vJURQDqF2EmpguftTSJn8Lm52Kp6ggnIfW0edG5WUTEcTTXQ95Rnnv90afj500IvWAV9dbcL8mt+fQSKMqUVLQveL0yKzVMJcqk146j/exrOBKsNRavK4aGYOeSfviFZyjNVGLApsk8Wu+AwvYQinK400b7gYe8qUl52PEj9sBaSRb0RD262vQ7LX3dKuskUNuaiJRwwV9qdE+5qT7JCAEOYV52NWVFQ8FmyhcWuY2lN2ukEKdeS6yWRU1i50eUxlKx13I73ZMHpS+MpgJ8yLCCylOY+so9hky8n0NYPKHV7EcuHa/L0KnxnUi2dN1L0zKlS7k+KV9bZ2bP4P2H5pgupe08rOdo4Bj/hqLTEClmzz+8mn9eZrF+92MglbYW/rMUV2aogZ3E5Juychq0vBIQR2n+3K5d/1uZQ/bgL7GiefG5nwrG51I5ffS6D6dZVt4mEugr75Acjbq/qm/Va9cBAqOUEOt5Ssgfvwop164wukLuSd8C0QfEg8ZIA2kkrnLdYzKzKYwTvUCaxfLYw3LyrLzHfIENRzY8wtN6vyB71Km0AJfZJl26/X0OIjkW8IAoRbbj/b0H5Pu0EL7a4XfoO5v0Pz7o/x22Nb8mnCwVMIBXgJoQSPV5ec1MRoUOEJwg2QkQP6Tms6VKSKYp4QDsYUyUFLdYfH/fBfPxHi/Fkq6iTtfmP9BGNYhneGaMn/dzK4E+OmFzgIw3UgtQZCt+OIqdW+VZbm2frhNlF3TIrLFvpQwz5kVg2f4OHMgofNk25Bm3xW/NCGB2+P9oeiMuWvN5EnS1QpXYYGka787JJ3PDak4+RAGwEUuN0IJ29ujVbyBg5JLD8SoaCDoBRJPqumADnQqa2A1v9CYqoEg1dmXYz0VtSm4usDs7YW3JORZ4KhVDfAJxKQqbBjocLhUnHEhAiL8EPnCH4jqgrZ+gtyMqbocAhpkAcVU1MwDgBcymkckA7YcyxXkkXfazWsMdo7yp9P87123JAtJQvIVTCNDBnRzt2FpiN8ubtfJ76kK1DnHbYJzlgcUz5PXHZVZfQsSGBonWcQdV04VV+Rscjpp4miErv+/8fi9APb1D4IZXP6UGrKGJkPFAzNa0AemGb+0P8yMhscrDV5i7OhLA5SlhpXlGJLFwIcCEyTwYAehBBXOTmUYbMQhGBPaGqtaqVvjNWN0flCPUt7qT+MgWOqbpIigSVdz9hhi167WDEeVhDeO11KEZHMMc17J5rGIhhbEV0SYAU4WPqLP/PqeNckjnllo8MMJpIGPS62ROLArETQdXf+1UJvCrzkC4D0nJOVEXcEiJZpuHadOjS/wFBICizEY/90C4Bilh0cte4eKVEulU8NItgHVQAG/cIf99MRDqHI79+WaZSlg1Y6J1LBgIGd/ii9CeWWDVaytKEicwR7wKnfbfVsOblELVZ+gdtklkjiHV9rIFSLEaLB78HCGrffp/sJJdw6mKJeyztj8MvPWOOwXX4ZVLEw3L7K6aBc4hOjm15Om3ZSsvZbO0qmz/zmIKZbJJ2Jfio27y6PL5dGdtDt9UeV+a/TNhrJvRLWnk58tTH7OF5DgEZiqihOg3pNhSt2ivLwzw==",
        //				"nonce": "dd9b891f0424"
        //			},
        JSONObject jsonObject = JSONObject.parseObject(body);
        JSONArray jsonArray = jsonObject.getJSONArray("data");
        if (jsonArray.size() < 1) return;
        String serialNo = jsonArray.getJSONObject(0).getString("serial_no");
        String associatedData = jsonArray.getJSONObject(0).getJSONObject("encrypt_certificate").getString("associated_data");
        String ciphertext = jsonArray.getJSONObject(0).getJSONObject("encrypt_certificate").getString("ciphertext");
        String nonce = jsonArray.getJSONObject(0).getJSONObject("encrypt_certificate").getString("nonce");
        if (!serialNo.equals(GlobalConstants.WXPAY_PLATFORM_NO)) {
            GlobalConstants.WXPAY_PLATFORM_NO = serialNo;
            GlobalConstants.associated_data = associatedData;
            GlobalConstants.ciphertext = ciphertext;
            GlobalConstants.nonce = nonce;
            AesUtil util = new AesUtil(GlobalConstants.WX_PAY_PRIVATE_KEY.getBytes());
            try {
                String decodeData = util.decryptToString(GlobalConstants.associated_data.getBytes(), GlobalConstants.nonce.getBytes(), ciphertext);
                GlobalConstants.PLATFORM_CERTIFICATE = decodeData;
                System.out.println(decodeData);
            } catch (GeneralSecurityException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private String wxPayRequestGet(String requestUrl, Map<String, String> map) {
        try {
            OkHttpClient.Builder builder = getUnsafeOkHttpClient().newBuilder();
            builder.connectTimeout(60, TimeUnit.SECONDS)
                    .readTimeout(60, TimeUnit.SECONDS)
                    .writeTimeout(60, TimeUnit.SECONDS)
                    .retryOnConnectionFailure(false);
            if (map != null) {
                for (Map.Entry<String, String> queryMap : map.entrySet()) {
                    requestUrl = requestUrl + "?" + queryMap.getKey() + "=" + queryMap.getValue();
                }
            }
            Request request = new Request.Builder()
                    .url(requestUrl)
                    .addHeader("Accept", MediaType.get("application/json").toString())
                    .addHeader("Content-type", "application/json")
                    .addHeader("Wechatpay-Serial", GlobalConstants.WXPAY_PLATFORM_NO)
                    .addHeader("Authorization", "WECHATPAY2-SHA256-RSA2048 "
                            + getToken(HttpMethod.GET.name(), requestUrl, ""))
                    .build();

            Response response = null;
            String body = "";
            System.out.println(requestUrl);
            response = builder.build().newCall(request).execute();
            body = response.body().string();
            System.out.println(body);
            return body;
        } catch (
                IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String okHttpRequestGet(String requestUrl) {
        OkHttpClient.Builder builder = getUnsafeOkHttpClient().newBuilder();
        builder.connectTimeout(60, TimeUnit.SECONDS)
                .readTimeout(60, TimeUnit.SECONDS)
                .writeTimeout(60, TimeUnit.SECONDS)
                .retryOnConnectionFailure(false);
        Request request = new Request.Builder()
                .url(requestUrl)
                .build();
        Response response = null;
        String body = "";
        try {
            System.out.println(requestUrl);
            response = builder.build().newCall(request).execute();
            body = response.body().string();
            System.out.println(body);
        } catch (
                IOException e) {
            e.printStackTrace();
        }
        return body;
    }

    public String okHttpRequestPost(String requestUrl, String outputStr) {
        OkHttpClient.Builder builder = getUnsafeOkHttpClient().newBuilder();
        builder.connectTimeout(60, TimeUnit.SECONDS)
                .readTimeout(60, TimeUnit.SECONDS)
                .writeTimeout(60, TimeUnit.SECONDS)
                .retryOnConnectionFailure(false);
        //        System.getProperties().put("proxySet", "true");
//        System.getProperties().put("proxyHost", "127.0.0.1");
//        System.getProperties().put("proxyPort", "1080");
        try {
            Request request = new Request.Builder()
                    .addHeader("Accept", MediaType.get("application/json").toString())
                    .addHeader("Content-type", "application/json")
                    .addHeader("Wechatpay-Serial", GlobalConstants.WXPAY_PLATFORM_NO)
                    .addHeader("Authorization", "WECHATPAY2-SHA256-RSA2048 "
                            + getToken(HttpMethod.POST.name(), requestUrl, outputStr))
                    .url(requestUrl)
                    .method(HttpMethod.POST.name(), RequestBody.create(MediaType.get("application/json"), outputStr))
                    .build();

            System.out.println(outputStr);
            Response response = null;
            String body = "";
            System.out.println(requestUrl);
            response = builder.build().newCall(request).execute();
            body = response.body().string();
            System.out.println(body);
            return body;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;

    }

    //okHttp3添加信任所有证书

    public static OkHttpClient getUnsafeOkHttpClient() {
        try {
            final TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
                        }

                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
                        }

                        @Override
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return new java.security.cert.X509Certificate[]{};
                        }
                    }
            };
            final SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
            final javax.net.ssl.SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
            OkHttpClient.Builder builder = new OkHttpClient.Builder();
            builder.sslSocketFactory(sslSocketFactory);
            builder.hostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });

            return builder.build();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    private String getToken(String method, String url, String body) throws UnsupportedEncodingException {

        String nonceStr = "cloudkintowexinpayToamount";
        long timestamp = System.currentTimeMillis() / 1000;
        String message = buildMessage(method, HttpUrl.parse(url), timestamp, nonceStr, body);
        String signature = sign(message.getBytes("utf-8"));

        return "mchid=\"" + GlobalConstants.APP_ID_WX_PAY + "\","
                + "nonce_str=\"" + nonceStr + "\","
                + "timestamp=\"" + timestamp + "\","
//                + "serial_no=\"" + GlobalConstants.WXPAY_MINE_NO + "\"";
                + "serial_no=\"" + GlobalConstants.WXPAY_MINE_NO + "\","
                + "signature=\"" + signature + "\"";
    }

    private String sign(byte[] message) {
        try {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(getPrivateKey(signPath + "apiclient_key.pem"));
            sign.update(message);
            return Base64.getEncoder().encodeToString(sign.sign());
        } catch (SignatureException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * RSA验签名检查
     *
     * @param content 待签名数据
     * @param sign    签名值
     * @return 布尔值
     */
    public static boolean verify(String content, String sign) {
        try {
            PublicKey pubKey = getCertificate(GlobalConstants.PLATFORM_CERTIFICATE.getBytes()).getPublicKey();
//            PublicKey pubKey = getPublicKey(GlobalConstants.PLATFORM_CERTIFICATE);
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initVerify(pubKey);
            signature.update(content.getBytes());
            boolean bverify = signature.verify(Base64.getDecoder().decode(sign));
            return bverify;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    private String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
        String canonicalUrl = url.encodedPath();
        if (url.encodedQuery() != null) {
            canonicalUrl += "?" + url.encodedQuery();
        }
        System.out.println(method + "\n"
                + canonicalUrl + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n");
        return method + "\n"
                + canonicalUrl + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
    }

    public static String rsaEncryptOAEP(String message, X509Certificate certificate)
            throws IllegalBlockSizeException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());

            byte[] data = message.getBytes("utf-8");
            byte[] cipherdata = cipher.doFinal(data);
            return Base64.getEncoder().encodeToString(cipherdata);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("无效的证书", e);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
        }
    }

    /**
     * 根据公钥字符串获取公钥并加密
     *
     * @param message   加密内容
     * @param cerString 公钥字符串
     * @return 加密后的字符串
     */
    public String rsaEncryptOAEP(String message, String cerString)
            throws IllegalBlockSizeException, IOException {
        //获取pubkey字符串内容
//        String key = getPubKeyContentString(cerPath);

        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, getCertificate(cerString.getBytes()));
            byte[] data = message.getBytes("utf-8");
            byte[] cipherdata = cipher.doFinal(data);
            return Base64.getEncoder().encodeToString(cipherdata);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("无效的证书", e);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据公钥字符串获取公钥
     *
     * @param key 公钥字符串
     * @return 返回公钥
     * @throws Exception
     */
    public static PublicKey getPublicKey(String key) throws Exception {
        byte[] keyBytes;
        String pubKey = key.replace("-----BEGIN CERTIFICATE-----", "")
                .replace("-----END CERTIFICATE-----", "")
                .replaceAll("\\n", "");
        keyBytes = (Base64.getDecoder().decode(pubKey));
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }

    /**
     * 实例化公钥
     *
     * @return
     */
    private PublicKey getPubKey(String content) {
        PublicKey publicKey = null;
        try {
            String pubKey = content.replace("-----BEGIN CERTIFICATE-----", "")
                    .replace("-----END CERTIFICATE-----", "")
                    .replaceAll("\\s+", "");
            java.security.spec.X509EncodedKeySpec bobPubKeySpec = new java.security.spec.X509EncodedKeySpec(
                    new BASE64Decoder().decodeBuffer(pubKey));
            // RSA对称加密算法
            java.security.KeyFactory keyFactory;
            keyFactory = java.security.KeyFactory.getInstance("RSA");
            // 取公钥匙对象
            publicKey = keyFactory.generatePublic(bobPubKeySpec);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return publicKey;
    }

    /**
     * 获取商户私钥。
     *
     * @param filename 私钥文件路径  (required)
     * @return 私钥对象
     */
    public PrivateKey getPrivateKey(String filename) throws IOException {

        String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");

            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }

    /**
     * 根据pem公钥路径获取公钥字符串
     *
     * @param cerPath 路径
     * @return 公钥字符串
     */
    public String getPubKeyContentString(String cerPath) {

        // 读取本机存放的PKCS8证书文件
        String currentPath = Thread.currentThread().getContextClassLoader().getResource(cerPath).getPath();
        try {
            //读取pem证书
            BufferedReader br = new BufferedReader(new FileReader(currentPath));

            StringBuffer publickey = new StringBuffer();
            String line;

            while (null != (line = br.readLine())) {
                if ((line.contains("BEGIN PUBLIC KEY") || line.contains("END PUBLIC KEY")))
                    continue;
                publickey.append(line);
            }
            return publickey.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取证书。
     *
     * @param buf 证书内容 (required)
     * @return X509证书
     */
    public static X509Certificate getCertificate(byte[] buf) throws IOException {
        ByteArrayInputStream bis = new ByteArrayInputStream(buf);

        try {
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
            cert.checkValidity();
            return cert;
        } catch (CertificateExpiredException e) {
            throw new RuntimeException("证书已过期", e);
        } catch (CertificateNotYetValidException e) {
            throw new RuntimeException("证书尚未生效", e);
        } catch (CertificateException e) {
            throw new RuntimeException("无效的证书文件", e);
        } finally {
            bis.close();
        }
    }

    public static void main(String[] args) {
        System.out.println(verify("1614241142\n" +
                        "67dIClSJHNJUsni2LDgjS58I2U09Cvb8\n" +
                        "{\"create_time\":\"2021-02-25T16:19:01+08:00\",\"event_type\":\"TRANSACTION.SUCCESS\",\"id\":\"cc21dcf6-7caa-512c-aa39-244579674ce1\",\"resource\":{\"algorithm\":\"AEAD_AES_256_GCM\",\"associated_data\":\"transaction\",\"ciphertext\":\"jVXleY7dNObrvQkGcXIryqsv0+Z2zlHfWeuwMXUJ20GmbQnx1k4Jh9h7mBkUpcWY81JDIvtUAjzgxn+bAKuEoXlDpr2sZqerppCIUA0NAno9+/gFk4tn+0C/CZ4RmfgnBz9qOZ5/dsZCCj4Y1W2xRryC+XIRu5YKSiCV66bDvyXAEkFXldOri/6Ld0495jERoeLXJqiKcmv49+InjBr/FZpLBZMequipHB8Vnkes/ax32gAbBD9l8zQoTR7HAdYT5KTEmfFpSanbXeHuz6WQEK2CsoiBPmDw53n+xvmbtCvYW44tr0jny3mu/NGz5NEB+HRnlJJWFo9EpNvagKeP+2WfNnfUwzxlcW6dhPvBJGdxCjNgAlfpwhqebhIvxI5JLAgEyiu3Qp56Ej5ytpW3T8zr1eTNpyiOfCyUekBEIov0b0S4bF2InxDzFGe84BZ6iiRqibT0wiyEQao55mFG+kFmX8bTrUd4DGQlb7dttiaDFo2snVeNhaQmdU89CvVOhLQfEVTRM99Plw4D1yktkWlrXBtdSmA892OXORDnhcfP7Bs7b0PwqoDG7cB4\",\"nonce\":\"o0OkDZ8zMwCc\",\"original_type\":\"transaction\"},\"resource_type\":\"encrypt-resource\",\"summary\":\"支付成功\"}\n",
                "LKcFnf/9YxrY1PgUEI3tFSnemkJS5RVd+qVRlQJh+Skt3eecR2KPFJ0PeY4M8CF713Vb8QCsiUsZZHcmbnFqvIxqXuRmoGUjYOhJdYHAWqxLtV4cZXsX4RpEfeOiz28IPFxmZVMsNbFv8A6fk6EClsddt8JYgCcYARPf27G5ajutR7wTDSEsFf9iN8asnF62hTKpFyg512TO476bzRlwVNrKmaBPnG/YaWXfGKeQ4Pb8h9u9ZPx1pjpKzgvHxHIL4DXq/VRCe4x/s9jgpmHsvOPQAFgJw6zyqLCYUZy7jc6L5QALZHfOU5Bh4FRckUvG6NZEVFrIZ6s0nPVC83+KEg=="));
    }

    private static PublicKey getPubKey() {
        PublicKey publicKey = null;
        try {
            // 自己的公钥(测试)
//            String pubKey ="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVRiDkEKXy/KBTe+UmkA+feq1zGWIgBxkgbz7aBJGb5+eMKKoiDRoEHzlGndwFKm4mQWNftuMOfNcogzYpGKSEfC7sqfBPDHsGPZixMWzL3J10zkMTWo6MDIXKKqMG1Pgeq1wENfJjcYSU/enYSZkg3rFTOaBSFId+rrPjPo7Y4wIDAQAB";
            String pubKey = GlobalConstants.PLATFORM_CERTIFICATE.replace("-----BEGIN CERTIFICATE-----", "")
                    .replace("-----END CERTIFICATE-----", "")
                    .replaceAll("\\s+", "");
            ;
            java.security.spec.X509EncodedKeySpec bobPubKeySpec = new java.security.spec.X509EncodedKeySpec(
                    new BASE64Decoder().decodeBuffer(pubKey));
            // RSA对称加密算法
            java.security.KeyFactory keyFactory;
            keyFactory = java.security.KeyFactory.getInstance("RSA");
            // 取公钥匙对象
            publicKey = keyFactory.generatePublic(bobPubKeySpec);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return publicKey;
    }
}